package cn.yshujia.utils;

import cn.yshujia.config.JwtConfig;
import cn.yshujia.constant.SystemConst;
import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import io.jsonwebtoken.security.SecureDigestAlgorithm;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.crypto.SecretKey;
import java.nio.charset.StandardCharsets;
import java.time.Instant;
import java.util.Date;
import java.util.Objects;
import java.util.UUID;

/**
 * @author YShuJia
 * @description
 * @create 2024/4/23
 */

@Slf4j
@Component
public class JwtTokenUtils {
	
	private static JwtConfig jwtConfig;
	/**
	 * 加密算法
	 */
	private final static SecureDigestAlgorithm<SecretKey, SecretKey> ALGORITHM = Jwts.SIG.HS256;
	/**
	 * jwt签发者
	 */
	private final static String JWT_ISS = SystemConst.AUTHOR;
	/**
	 * jwt主题
	 */
	private final static String SUBJECT = "Peripherals";
	
	@Autowired
	public JwtTokenUtils(JwtConfig jwtConfig) {
		JwtTokenUtils.jwtConfig = jwtConfig;
	}
	
	/*
	这些是一组预定义的声明，它们 不是强制性的，而是推荐的 ，以 提供一组有用的、可互操作的声明 。
	iss: jwt签发者
	sub: jwt所面向的用户
	aud: 接收jwt的一方
	exp: jwt的过期时间，这个过期时间必须要大于签发时间
	nbf: 定义在什么时间之前，该jwt都是不可用的.
	iat: jwt的签发时间
	jti: jwt的唯一身份标识，主要用来作为一次性token,从而回避重放攻击
	*/
	public static String genAccessToken(Long id) {
		// 令牌id
		String uuid = UUID.randomUUID().toString();
		Date exprireDate = Date.from(Instant.now().plusSeconds(jwtConfig.getExpire() * 60L));
		
		return Jwts.builder()
				// 设置头部信息header
				.header()
				.add("typ", "JWT")
				.add("alg", "HS256")
				.and()
				// 设置自定义负载信息payload
				.claim("id", id)
				// 令牌ID
				.id(uuid)
				// 过期日期
				.expiration(exprireDate)
				// 签发时间
				.issuedAt(new Date())
				// 主题
				.subject(SUBJECT)
				// 签发者
				.issuer(JWT_ISS)
				// 签名
				.signWith(Keys.hmacShaKeyFor(jwtConfig.getSecret().getBytes(StandardCharsets.UTF_8)), ALGORITHM)
				.compact();
	}

	public static Long getUserId(String jwt) {
		if (StringUtils.isEmpty(jwt)) {
			return null;
		}
		jwt = jwt.replace(SystemConst.TOKEN_PREFIX, "");
		return (Long) Objects.requireNonNull(parseClaim(jwt)).get("id");
	}

	public static Long getUserId(HttpServletRequest req) {
		String jwt = req.getHeader(SystemConst.TOKEN_KEY);
		return getUserId(jwt);
	}

	/**
	 * 解析token
	 */
	public static Claims parseClaim(String token) {
		try {
			return Jwts.parser()
					.verifyWith(Keys.hmacShaKeyFor(jwtConfig.getSecret().getBytes(StandardCharsets.UTF_8)))
					.build()
					.parseSignedClaims(token)
					.getPayload();
		} catch (ExpiredJwtException e) {
			log.warn("Token 已过期: {}", maskToken(token), e);
			// 即使过期，仍然返回Claims对象
			return e.getClaims();
		} catch (UnsupportedJwtException | MalformedJwtException e) {
			log.error("Token 格式不正确: {}", maskToken(token), e);
			return null;
		} catch (IllegalArgumentException e) {
			log.error("Token 为空或无效: {}", maskToken(token), e);
			return null;
		}
	}

	/**
	 * 掩盖token信息以保护敏感数据
	 *
	 * @param token token
	 * @return 掩盖后的token
	 */
	private static String maskToken(String token) {
		if (token == null || token.isEmpty()) {
			return "空token";
		}
		// 只显示前10个字符
		return token.substring(0, Math.min(token.length(), 10)) + "...";
	}

	//判断是否过期
	public static boolean isRefresh(String jwt) {
		if (null == jwt) {
			return true;
		}
		jwt = jwt.replace(SystemConst.TOKEN_PREFIX, "");
		Date expirationDate = Objects.requireNonNull(parseClaim(jwt)).getExpiration();
		return expirationDate.getTime() / 1000 - jwtConfig.getRefresh() >= new Date().getTime() / 1000;
	}

	//判断是否过期
	public static boolean isExpired(String jwt) {
		jwt = jwt.replace(SystemConst.TOKEN_PREFIX, "");
		Date expirationDate = Objects.requireNonNull(parseClaim(jwt)).getExpiration();
		String date = TimeUtils.getParallelTime(expirationDate);
		return date.compareTo(TimeUtils.getParallelTime()) < 0;
	}
}
